#ifndef TMS_UI_H_
#define TMS_UI_H_

#pragma once

#include <iostream>
#include <string>
#include <sstream>
#include <vector>
#include <ctime>
#include <iomanip>

#include "tms_definitions.hpp"

// 跨平台支持
#ifdef TMS_WIN32_PLATFORM
// Windows 平台
#include <windows.h>
#include <conio.h>
#endif

#ifdef TMS_POSIX_PLATFORM
// Linux 平台
#include <sys/ioctl.h>
#include <unistd.h>
#include <termios.h>
#include <sys/select.h>
#include <sys/time.h>
#endif

enum UiOption
{
    OPT_YES,
    OPT_NO
};

enum LogInType
{
    ADMIN_L,
    CUSTO_L,
    NO_LOGIN
};

class Application;

namespace tms
{
    class Info;
    class StringI;
    class PasswdI;
    class IntI;
    class DoubleI;
    class OptionI;
}

/*
信息输出对象，在信息区显示内容

mw << tms::Info("Infomation here");
*/
class tms::Info
{
public:
    std::string info;

    Info(const std::string &s) : info(s) {};
};

/*
字符串输入对象

tms::StringI input("Input string");
mw >> input;
*/
class tms::StringI
{
public:
    std::string promote;
    std::string i;

    StringI(const std::string &s) : promote(s) {};
};

/*
密码输入输入对象，输入字符串并关闭回显

tms::PasswdI input("Input password");
mw >> input;
*/
class tms::PasswdI
{
public:
    std::string promote;
    std::string i;

    PasswdI(const std::string &s) : promote(s) {};
};

/*
整数输入对象
这个类在输入不能被转换时会抛出 invalid_argument 异常

tms::IntI input("Input int");
mw >> input;
*/
class tms::IntI
{
public:
    std::string promote;
    int i;

    IntI(const std::string &s) : promote(s), i() {};
};

/*
双精度浮点数输入对象
这个类在输入不能被转换时会抛出 invalid_argument 异常

tms::DoubleI input("Input double");
mw >> input;
*/
class tms::DoubleI
{
public:
    std::string promote;
    double i;

    DoubleI(const std::string &s) : promote(s), i() {};
};

/*
确认选项输入对象
这个类在输入不能被转换时会抛出 invalid_argument 异常

tms::OptionI input("Input option[Y/n]");
mw >> input;
*/
class tms::OptionI
{
public:
    std::string promote;
    UiOption i;

    OptionI(const std::string &s) : promote(s), i() {};
};

/*
TMS 窗口
此类处理系统差异得到终端形状，实现交互界面绘制，获取用户初始输入
窗口是重新开发的 UI 接口，集中精简了 UI 的字符界面控制能力，简化了使用方法
提高了代码的复用程度，降低了一半调用和程序的结构复杂度

主要特性：
    这是符合 C++ 引用/重载编写风格的一段代码
    TmsWin 托管了所有显示元素，交互对象不再需要刷新全部元素，减小了内存开销
    使用重载方法简化了调用，压缩调用代码行，不需要使用指针，减小内存隐患
    新的交互对象使用函数方法设置值而不是简单集成值，具有更好的灵活性

e.g.
    display << tms::Info("WELCOME using tms!");
    display >> input;

一个用户接口被分为三个区域：顶部状态栏 信息 提示 输入
顶部状态栏显示应用程序的菜单级别，当前时间，版本号，通过 API 自动刷新
信息区从屏幕上端开始生长，直到屏幕倒数第二行前，显示该页面的主要内容
提示区在屏幕倒数第二行，作为简短的提醒用户输入内容格式的提醒
输入区在屏幕最后一行，行为与普通输入相同
| Menu Location Field | Time | Application version |
| Infomation field                                 |
| ......                                           |
| Promote string                                   |
| [Input field]                                    |

此类包含了跨平台绘制支持，可以在 windows 10+ 和 POSIX 平台上绘制交互界面
*/
class TmsWin
{
private:
    /* 显示元素，为了简化编写，此处属性直接由友元函数访问 */
    Application *app;
    std::string info_str;
    std::string promote_str;
    std::string menu_location_str;
    std::string time_str;
    std::string app_version_str;

    /* 输入缓冲和输入回显设置 */
    std::string input_l_cache;
    int is_input_echo;

    /* 窗口属性 */
    int win_w, win_h;

protected:
    /* 计算 info 区域的文本字符高度 */
    inline unsigned int get_line_cnt();

    /* 刷新顶部信息区 */
    void update_top_bar();

    /* 刷新界面 */
    void refresh();

public:
    TmsWin(Application *app);
    ~TmsWin();

    friend TmsWin &operator<<(TmsWin &w, const tms::Info &dw);
    friend TmsWin &operator>>(TmsWin &w, tms::StringI &dw);
    friend TmsWin &operator>>(TmsWin &w, tms::PasswdI &dw);
    friend TmsWin &operator>>(TmsWin &w, tms::IntI &dw);
    friend TmsWin &operator>>(TmsWin &w, tms::DoubleI &dw);
    friend TmsWin &operator>>(TmsWin &w, tms::OptionI &dw);
    friend TmsWin &operator>>(TmsWin &w, const tms::StringI &dw);
};

/*
基本菜单节点类
此类实现菜单节点基本逻辑功能，包括添加节点，获取绝对路径，节点的执行方法
菜单节点用于完全控制程序的执行结构，这是一个树形菜单结构
由于应用程序类在每个层级设计有区别，所以应用程序是私有的，保证一致性，避免混乱的动态转换
基础节点只有一个指向父节点的指针和一个指向子节点的向量
节点名同时充当节点 ID，在 get_locator 中被使用，配合 get_n_name 可以实现节点验证
*/
class BaseMenuNode
{
private:
    Application *app;
    BaseMenuNode(const BaseMenuNode &other);

protected:
    std::string n_name;
    BaseMenuNode *par_node;
    std::vector<BaseMenuNode *> child_node;

public:
    explicit BaseMenuNode(Application *app, const std::string &n_name, BaseMenuNode *par_node);

    /* 虚析构函数，对菜单树的内存管理删除从根节点开始 */
    virtual ~BaseMenuNode();

    /* 虚执行方法，每个节点重新实现，节点方法访问都通过指针进行，确保访问相应对象的方法 */
    virtual int execute();

    /* 添加子节点 */
    void add_child(BaseMenuNode *child_node);

    /* 获取节点名称 */
    std::string get_n_name() const;

    /* 获取当前节点定位器，返回一个从根节点开始，包含当前节点的 ID 向量 */
    std::vector<std::string> get_locator() const;

    /* 高级接口，返回一个被格式化后的字符串式定位器，可以通过参数定制分隔符 */
    std::string path2string(const std::string spacer = " > ") const;
};

class SelectionExitError : std::runtime_error
{
public:
    SelectionExitError(const std::string &res) : std::runtime_error(res) {};
};

/*
选择菜单节点
选择菜单节点在基类的基础上引入菜单选择交互，根据选择结果跳转到对应节点
菜单节点有复杂的执行逻辑和控制方式
首先菜单节点初始化成功后调用 init_loop 尝试初始化循环，init_loop 被用来插入一些验证类型的方法，可以在进入循环前执行
然后执行循环体，每一次循环前使用 begin_loop 开始，执行选择之后使用 after_loop 结束
如果循环退出，一定会执行 exit_loop 中的内容

循环的退出使用 SelectionExitError 控制，需要退出循环时直接抛出异常即可，异常中的提示字符串会被忽略。
*/
class SelectionNode : public BaseMenuNode
{
private:
    Application *app;

    SelectionNode(const SelectionNode &other);

protected:
public:
    SelectionNode(Application *app, const std::string &n_name, BaseMenuNode *par_node);

    virtual void befor_loop();

    virtual void after_loop();

    virtual void init_loop();

    virtual void exit_loop();

    virtual int execute();
};

/*
登录状态
记录登录状态的用户 ID
用户 ID 会在多级菜单中被使用
*/
class TmsLoginStatus
{
private:
    std::string user_id;
    LogInType lt;

protected:
public:
    TmsLoginStatus() : user_id(""), lt(NO_LOGIN) {};
    virtual ~TmsLoginStatus() {};

    void set_user_id(const std::string &uid) { this->user_id = uid; };
    std::string get_user_id() const { return this->user_id; };
    void set_login_status(const LogInType &nlt) { this->lt = nlt; };
    LogInType get_login_status() const { return this->lt; };
};

/*
主程序框架
主程序框架存放程序主函数和一些公用的类实例，例如显示绘制器和状态储存器
使用主程序类可以提供更好的封装性
*/
class Application
{
private:
    Application(const Application &);

protected:
public:
    BaseMenuNode *menu;
    BaseMenuNode *act_node;
    TmsLoginStatus *l_status;
    // main window
    TmsWin mw;

    explicit Application();
    virtual ~Application();

    /* 主循环，重载实现应用功能 */
    virtual int mainloop();

    /* 获取当前时间字符串，前端专用 */
    std::string get_current_time();

    /* 获取当前活跃菜单路径 */
    std::string get_menu_location();

    /* 获取应用程序版本号，理论上是语义化版本加 git hash 编译序号 */
    std::string get_app_version();

    /* 设置当前活跃节点 */
    void set_active_node(BaseMenuNode *n);
};

#endif
